#pragma once
#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/stat.h>
#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING
#include <experimental/filesystem>
#include <memory>

namespace Cloud
{
    namespace fs = std::experimental::filesystem;

    class FileUtils
    {
    public:
        FileUtils(const std::string& filePath) : _filePath(filePath) {}
        int64_t getSize() const
        {
            struct stat file_info;
            if (stat(_filePath.c_str(), &file_info) == -1)
            {
                std::cout << _filePath << ":";
                perror("FileUtils::GetSize::stat");
                return -1;
            }
            return file_info.st_size;
        }

        time_t getMTime() const
        {
            struct stat file_info;
            if (stat(_filePath.c_str(), &file_info) == -1)
            {
                perror("FileUtils::GetMTime::stat");
                return -1;
            }
            return file_info.st_mtime;
        }

        time_t getATime() const
        {
            struct stat file_info;
            if (stat(_filePath.c_str(), &file_info) == -1)
            {
                perror("FileUtils::GetATime::stat");
                return -1;
            }
            return file_info.st_atime;
        }

        std::string getFileName() const
        {
            size_t pos = _filePath.find_last_of("\\");
            if (pos == std::string::npos) {
                return _filePath;
            }
            return _filePath.substr(pos + 1);
        }

        bool getContentFromPos(std::string* pContent, size_t pos, size_t len) const
        {
            if (!pContent)
                return false;
            size_t size = getSize();
            if (size == -1)
                return false;
            if (size > pos && len > size - pos)
            {
                std::cerr << "GetContentFromPos: pos + len > size\n";
                return false;
            }

            std::ifstream ifs(_filePath, std::ios::binary);
            if (!ifs)
            {
                std::cerr << "GetContentFromPos: open file error\n";
                return false;
            }

            ifs.seekg(pos, std::ios_base::beg);

            pContent->resize(len);

            ifs.read(&((*pContent)[0]), len);
            if (!ifs)
            {
                std::cerr << "GetContentFromPos: read file error\n";
                return false;
            }
            return true;
        }

        bool getContent(std::string* pContent) const
        {
            return getContentFromPos(pContent, 0, getSize());
        }

        bool setContent(const std::string& content)
        {
            std::ofstream ofs(_filePath, std::ios::binary);
            if (!ofs)
            {
                std::cerr << "SetContent: open file error\n";
                return false;
            }

            ofs.write(&content[0], content.size());

            if (!ofs)
            {
                std::cerr << "SetContent: write file error\n";
                return false;
            }

            return true;
        }

        bool exists() const
        {
            return fs::exists(_filePath);
        }

        bool remove()
        {
            return !exists() || fs::remove(_filePath);
        }

        bool createDirectory()
        {
            if (exists())
                return true;
            return fs::create_directories(_filePath);
        }

        void scanDirectory(std::vector<std::string>* pArr)
        {
            if (!pArr)
                return;
            for (auto& p : fs::directory_iterator(_filePath))
            {
                if (fs::is_directory(p))
                    FileUtils(p.path().string()).scanDirectory(pArr);
                else
                    pArr->push_back(p.path().string());
            }
        }

    private:
        std::string _filePath;
    };
} // namespace Cloud